package EA.operators.localsearch;
import EA.*;
import RKUjava.util.*;
import RKUjava.math.*;

/** Steepest Ascend/Descend for DiscreteNumericalGenomes. This local search 
    will examine the whole neighborhood and select the neighboring solution
    that gives the biggest performance increase.
    @see EA.DiscreteRNumGenome
    @see EA.DiscreteRNumFramework
*/
public class DRNumSteepestAscend extends NumLocalSearch
{

  /** Perform steepest ascend/descend local search on genome. 
      @param genome The DiscreteRNumGenome to optimize.
      @param params The parameters for the search operator. 
      <ul>
      <li>params[0] = # optimization steps (use 0 to optimize until stagnation)
      <li>params[1] = search interval for variables with stepsize=0 (scaled by
      length of interval.
      </ul>

  */
  public static final void localSearch(NumericalGenome genome, double[] params)
    {
      localSearch(genome, params, false);
    }

  /** Perform steepest ascend/descend local search on genome. 
      @param genome The DiscreteRNumGenome to optimize.
      @param params The parameters for the search operator. 
      <ul>
      <li>params[0] = # optimization steps (use 0 to optimize until stagnation)
      <li>params[1] = search interval for variables with stepsize=0 (scaled by
      length of interval.
      <li>params[2] = Precision for verbose printing. 
      <ul>
      <li>-1 = disable printing of position.
      <li>0 = full precision.
      </ul>
      </ul>
      @param verbose Print optimization information.
  */
  public static final void localSearch(NumericalGenome genome, double[] params, boolean verbose)
    {
      int i;
      DiscreteRNumFramework df;
      double[] stepsize;
      int maxtries, curtry;
      double[] curbest, curcenter, curtest;
      double bestfit, curfit;
      boolean updated, totalupdated, done, ismaximization;
      Interval[] intervals;


      df = (DiscreteRNumFramework)genome.framework;
      stepsize = df.discrete_stepsize;
      maxtries = (int)Math.rint(params[0]);
      curtry = 0;
      totalupdated = false;
      done = false;
      intervals = df.intervals;
      ismaximization = df.ismaximization;

      curcenter = genome.getPos();
      curbest = new double[curcenter.length];
      curtest = new double[curcenter.length];
      RKUArrays.copyArray(curcenter, curbest);
      RKUArrays.copyArray(curcenter, curtest);

      bestfit = df.getFitness(curbest);

      while (!done) {
	updated = false;

	for (i=0; i<curcenter.length; i++) {

	  if (stepsize[i]>0) {
	    // Search "below"
	    curtest[i] = curcenter[i]-stepsize[i];
	    if (curtest[i]>=intervals[i].min) {
	      curfit = df.getFitness(curtest);
	      //	      System.out.println("LS-"+curtry+": curfit="+curfit+"  pos="+RKUStringUtils.arrayToString(curtest));
	      if (ismaximization && curfit>bestfit) {
		bestfit = curfit;
		RKUArrays.copyArray(curcenter, curbest, 0, i);
		curbest[i] = curtest[i];
		updated = true;
	      }
	      else if (!ismaximization && curfit<bestfit) {
		bestfit = curfit;
		RKUArrays.copyArray(curcenter, curbest, 0, i);
		curbest[i] = curtest[i];
		updated = true;
	      }
	    }

	    // Search "above"
	    curtest[i] = curcenter[i]+stepsize[i];
	    if (curtest[i]<=intervals[i].max) {
	      curfit = df.getFitness(curtest);
	      //	      System.out.println("LS-"+curtry+": curfit="+curfit+"  pos="+RKUStringUtils.arrayToString(curtest));
	      if (ismaximization && curfit>bestfit) {
		bestfit = curfit;
		RKUArrays.copyArray(curcenter, curbest, 0, i);
		curbest[i] = curtest[i];
		updated = true;
	      }
	      else if (!ismaximization && curfit<bestfit) {
		bestfit = curfit;
		RKUArrays.copyArray(curcenter, curbest, 0, i);
		curbest[i] = curtest[i];
		updated = true;
	      }
	    }

	    // Reset curtest
	    curtest[i] = curcenter[i];
	  }
	  else if (params[1]>0) {
	    // No stepsize, search randomly if allowed.

	    // Search "below"
	    curtest[i] = curcenter[i]-intervals[i].getLength()*RKURandom.getUniformDistributed(0,params[1]);
	    if (curtest[i]>=intervals[i].min) {
	      curfit = df.getFitness(curtest);
	      if (ismaximization && curfit>bestfit) {
		bestfit = curfit;
		RKUArrays.copyArray(curcenter, curbest, 0, i);
		curbest[i] = curtest[i];
		updated = true;
	      }
	      else if (!ismaximization && curfit<bestfit) {
		bestfit = curfit;
		RKUArrays.copyArray(curcenter, curbest, 0, i);
		curbest[i] = curtest[i];
		updated = true;
	      }
	    }

	    // Search "above"
	    curtest[i] = curcenter[i]+intervals[i].getLength()*RKURandom.getUniformDistributed(0,params[1]);
	    if (curtest[i]<=intervals[i].max) {
	      curfit = df.getFitness(curtest);
	      if (ismaximization && curfit>bestfit) {
		bestfit = curfit;
		RKUArrays.copyArray(curcenter, curbest, 0, i);
		curbest[i] = curtest[i];
		updated = true;
	      }
	      else if (!ismaximization && curfit<bestfit) {
		bestfit = curfit;
		RKUArrays.copyArray(curcenter, curbest, 0, i);
		curbest[i] = curtest[i];
		updated = true;
	      }
	    }

	    // Reset curtest
	    curtest[i] = curcenter[i];
	  }
	}

	if (verbose) {
	  System.out.print("LS-"+curtry+": bestfit="+bestfit);
	  if (params[2]!=-1 && params[2]==0) {
	    System.out.print("  pos="+RKUStringUtils.arrayToString(curbest));
	  }
	  else if (params[2]!=-1) {
	    System.out.println("  pos="+RKUStringUtils.arrayToString(curbest,(int)params[2]));
	  }
	  System.out.println("");
	}

	curtry++;
	if (!updated || (maxtries>0 && curtry==maxtries)) {
	  done = true;
	}
	else {
	  RKUArrays.copyArray(curbest, curcenter);
	}
	
	if (updated) {
	  totalupdated = true;
	}
      }
      
      if (totalupdated) {
	genome.setPos(curbest);
      }
    }

}
